// #pragma once
// //.hpp--开源软件，声明和定义放在一起
// #include <iostream>
// #include <queue>
// #include <pthread.h>
// #include <stdlib.h>
// #include <time.h>
// #include <unistd.h>

// namespace ts
// {
//     const int capacity = 5;
//     template <class T>
//     class BlockQueue
//     {
//     private:
//         std::queue<T> _bq;//阻塞队列
//         int _cap; //队列容量
//         pthread_mutex_t _mtx;//保护临界资源
//         //当队列满的时候，就不应该在生产了，不应该在竞争锁了
//         //当队列空的时候，就不应该在消费了，不应该在竞争锁了
//         pthread_cond_t _is_full;//_bq满了，消费者在条件变量下等待
//         pthread_cond_t _is_empty;//_bq空的，生产者在条件变量下等待

//     private:
//         bool IsFull()
//         {
//             return _bq.size() == _cap;
//         }

//         void LcokQueue()
//         {
//             pthread_mutex_lock(&_mtx);
//         }

//         void UnLcokQueue()
//         {
//             pthread_mutex_unlock(&_mtx);
//         }

//         void ProducterWait()
//         {
//             //1.调用的时候，会首先释放锁，然后将自己挂起
//             //2.返回的时候，会自动竞争锁，获取到锁后，才能返回
//             pthread_cond_wait(&_is_empty, &_mtx);
//         }

//         bool IsEmpty()
//         {
//             return _bq.size() == 0;
//         }

//         void ConsumerWait()
//         {
//             pthread_cond_wait(&_is_full, &_mtx);
//         }

//         void WakeUpConsumer()
//         {
//             pthread_cond_signal(&_is_full);
//         }

//         void WakeUpProducter()
//         {
//             pthread_cond_signal(&_is_empty);
//         }

//     public:
//         BlockQueue()
//         :_cap(capacity)
//         {
//             pthread_mutex_init(&_mtx, nullptr);
//             pthread_cond_init(&_is_full, nullptr);
//             pthread_cond_init(&_is_empty, nullptr);
//         }

//         ~BlockQueue()
//         {
//             pthread_mutex_destroy(&_mtx);
//             pthread_cond_destroy(&_is_full);
//             pthread_cond_destroy(&_is_empty);
//         }
//         //const &:输入
//         //*:输出
//         //&：输入输出
//         void push(const T& in)
//         {
//             LcokQueue();
//             //临界区
//             //if(IsFull())
//             //这里需要使用循环方式， 保证退出循环的条件一定是条件不满足导致的，
//             //因为等待可能调用失败!!!
//             if(IsFull())
//             {
//                 //等待消费者消费
//                 ProducterWait();
//             }
//             //放数据到队列中
//             _bq.push(in);
//             WakeUpConsumer();
//             UnLcokQueue();
//         }

//         void pop(T* out)
//         {
//             //从队列中拿数据
//             LcokQueue();
//             if(IsEmpty())
//             {
//                 //等待生产者生产
//                 ConsumerWait();
//             }

//             *out = _bq.front();
//             _bq.pop();
//             //if(_bq.size() < _cap / 2)
//             WakeUpProducter();
//             UnLcokQueue();
//         }
//     };
// }

#pragma once
//.hpp--开源软件，声明和定义放在一起
#include <iostream>
#include <queue>
#include <pthread.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

namespace ts
{
    const int capacity = 5;
    template <class T>
    class BlockQueue
    {
    private:
        std::queue<T> _bq;//阻塞队列
        int _cap; //队列容量
        pthread_mutex_t _mtx;//保护临界资源
        //当队列满的时候，就不应该在生产了，不应该在竞争锁了
        //当队列空的时候，就不应该在消费了，不应该在竞争锁了
        pthread_cond_t _is_full;//_bq满了，消费者在条件变量下等待
        pthread_cond_t _is_empty;//_bq空的，生产者在条件变量下等待

    private:
        bool IsFull()
        {
            return _bq.size() == _cap;
        }

        void LcokQueue()
        {
            pthread_mutex_lock(&_mtx);
        }

        void UnLcokQueue()
        {
            pthread_mutex_unlock(&_mtx);
        }

        void ProducterWait()
        {
            //1.调用的时候，会首先释放锁，然后将自己挂起
            //2.返回的时候，会自动竞争锁，获取到锁后，才能返回
            pthread_cond_wait(&_is_empty, &_mtx);
        }

        bool IsEmpty()
        {
            return _bq.size() == 0;
        }

        void ConsumerWait()
        {
            pthread_cond_wait(&_is_full, &_mtx);
        }

        void WakeUpConsumer()
        {
            pthread_cond_signal(&_is_full);
        }

        void WakeUpProducter()
        {
            pthread_cond_signal(&_is_empty);
        }

    public:
        BlockQueue()
        :_cap(capacity)
        {
            pthread_mutex_init(&_mtx, nullptr);
            pthread_cond_init(&_is_full, nullptr);
            pthread_cond_init(&_is_empty, nullptr);
        }

        ~BlockQueue()
        {
            pthread_mutex_destroy(&_mtx);
            pthread_cond_destroy(&_is_full);
            pthread_cond_destroy(&_is_empty);
        }
        //const &:输入
        //*:输出
        //&：输入输出
        void push(const T& in)
        {
            LcokQueue();
            //临界区
            //if(IsFull())
            //这里需要使用循环方式， 保证退出循环的条件一定是条件不满足导致的，
            //因为等待可能调用失败!!!
            // if(IsFull())
            while(IsFull())
            {
                //等待消费者消费
                ProducterWait();
            }
            //放数据到队列中
            _bq.push(in);
            WakeUpConsumer();
            UnLcokQueue();
        }

        void pop(T* out)
        {
            //从队列中拿数据
            LcokQueue();
            //if(IsEmpty())
            while(IsEmpty())
            {
                //等待生产者生产
                ConsumerWait();
            }

            *out = _bq.front();
            _bq.pop();
            //if(_bq.size() < _cap / 2)
            WakeUpProducter();
            UnLcokQueue();
        }
    };
}